﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using PHPDecoder;

namespace CodeVisual
{
    class BadSettingFileException : Exception
    {
        public override string Message
        {
            get
            {
                return "Bad Setting File Format";
            }
        }
    }
    public enum OutputFormat
    {
        PDF = 0,
        SVG = 1,
        HTML = 2
    }

    public static class Setting
    {

        #region setting
        public static bool onlyFromProject = false;
        public static bool IgnoreUnusedVar = true;
        public static bool IgnoreUnusedFunc = true;
        // pocet volani, zaporne znamena menej a kladne znamena viac
        public static int NumCall = 0;
        public static int NumRead = 0;
        public static int NumWrite = 0;
        public static int NumChange = 0;
        public static OutputFormat format = OutputFormat.PDF;
        public static string outPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\CodeVisual";
        public static bool openAfterExport = true;
        public static string varname = null;
        public static string notvarname = null;
        public static string funcname = null;
        public static string notfuncname = null;
        public static string clsname = null;
        public static string notclsname = null;
        #endregion

        private const string settingfile = @"setting/setting.ini";
        static Dictionary<String, String> sett = new Dictionary<string, string>();

        /// <summary>
        /// Aplikovanie filtrov na vysledok
        /// </summary>
        /// <param name="res"></param>
        public static void ApplyFilter(ref AnalyseResult res)
        {
            foreach (ClassNode cnode in res.getAllClasses())
            {
                ClassNode cn = cnode;
                ApplyFilterClass(ref cn);
                foreach (FunctionNode fnode in cnode.getFunctions().Values)
                {
                    FunctionNode n = fnode;
                    ApplyFilterFunction(ref n);
                }
                foreach (VariableNode vnode in cnode.getVariables().Values)
                {
                    VariableNode n = vnode;
                    ApplyFilterVariable(ref n);
                }
            }
        }

        private static void ApplyFilter(ref DataNode node)
        {
            if (node.tp == NodeType.VAR)
            {
                VariableNode vnode = (VariableNode)node;
                ApplyFilterVariable(ref vnode);
            }
            else
                if (node.tp == NodeType.METHOD)
                {
                    FunctionNode fnode = (FunctionNode)node;
                    ApplyFilterFunction(ref fnode);
                }
                else
                {
                    ClassNode cnode = (ClassNode)node;
                    ApplyFilterClass(ref cnode);
                }
        }

        private static void ApplyFilterFunction(ref FunctionNode n)
        {
            if (NumCall == 0)
            {
                n.isoutput = true;
            }
            else
                if (NumCall < 0)
                {
                    int nc = -NumCall;
                    if (n.call <= nc)
                        n.isoutput = true;
                }
                else
                {
                    if (n.call >= NumCall)
                        n.isoutput = true;
                }

            if (IgnoreUnusedFunc)
            {
                if (n.call == 0)
                    n.isoutput = false;
            }

            if (funcname != null && funcname != "")
            {
                if (n.name.IndexOf(funcname) != -1) 
                    n.isoutput = true;
            }

            if (notfuncname != null && notfuncname != "")
            {
                if (n.name.IndexOf(funcname) != -1) n.isoutput = false;
            }

        }

        private static void ApplyFilterClass(ref ClassNode cnode)
        {
            if (clsname != null && clsname != "")
            {
                if (cnode.name.IndexOf(clsname) != -1) 
                    cnode.isoutput = true;
            }
            if (notclsname != null && notclsname != "")
            {
                if (cnode.name.IndexOf(notclsname) != -1) 
                    cnode.isoutput = false;
            }
        }

        private static void ApplyFilterVariable(ref VariableNode vnode)
        {
            // Jednotlive pravidla su vo vztahu OR, staci ak je splnene 1 pravidlo
            #region FILTER_OR
            bool b = false;
            vnode.isoutput = false;
            if (NumRead != 0)
            {
                if (NumRead < 0)
                {
                    if (vnode.read <= -NumRead)
                    {
                        vnode.isoutput = true;
                        b = true;
                    }

                }
                else
                {
                    if (vnode.read >= NumRead)
                    {
                        vnode.isoutput = true;
                        b = true;
                    }
                }
            }

            if (NumWrite != 0)
            {
                if (NumWrite < 0)
                {
                    if (vnode.write <= -NumWrite)
                    {
                        vnode.isoutput = true;
                        b = true;
                    }

                }
                else
                {
                    if (vnode.write >= NumWrite)
                    {
                        vnode.isoutput = true;
                        b = true;
                    }
                }
            }

            if (NumChange != 0)
            {
                if (NumChange < 0)
                {
                    if (vnode.change <= -NumChange)
                    {
                        vnode.isoutput = true;
                        b = true;
                    }

                }
                else
                {
                    if (vnode.change >= NumChange)
                    {
                        vnode.isoutput = true;
                        b = true;
                    }
                }
            }

            if (!b) vnode.isoutput = true;
            #endregion

            // Jednotlive filtre su vo vztahu AND, staci ak jedna nie je splnena, tak nebude uzol vystupny
            #region FILTER_AND
            //vnode.isoutput = true;
            //if (NumRead != 0)
            //{
            //    if (NumRead < 0)
            //    {
            //        if (vnode.read > -NumRead)
            //        {
            //            vnode.isoutput = false;
            //        }

            //    }
            //    else
            //    {
            //        if (vnode.read < NumRead)
            //        {
            //            vnode.isoutput = false;
            //        }
            //    }
            //}

            //if (NumWrite != 0)
            //{
            //    if (NumWrite < 0)
            //    {
            //        if (vnode.write > -NumWrite)
            //        {
            //            vnode.isoutput = false;
            //        }

            //    }
            //    else
            //    {
            //        if (vnode.write < NumWrite)
            //        {
            //            vnode.isoutput = false;
            //        }
            //    }
            //}

            //if (NumChange != 0)
            //{
            //    if (NumChange < 0)
            //    {
            //        if (vnode.change > -NumChange)
            //        {
            //            vnode.isoutput = false;
            //        }

            //    }
            //    else
            //    {
            //        if (vnode.change < NumChange)
            //        {
            //            vnode.isoutput = false;
            //        }
            //    }
            //}
            #endregion

            if (IgnoreUnusedVar)
            {
                if (vnode.read == 0 && vnode.write == 0 && vnode.change == 0) vnode.isoutput = false;
            }
            if (varname != null && varname != "")
            {
                if (vnode.name.IndexOf(varname) != -1)
                {
                    vnode.isoutput = true;
                }
            }
            if (notvarname != null && notvarname != "")
            {
                if (vnode.name.IndexOf(notvarname) != -1)
                {
                    vnode.isoutput = false;
                }
            }
        }

        /// <summary>
        /// Nacitanie nastavenia zo suboru setting/setting.ini
        /// </summary>
        /// <returns></returns>
        public static bool Load()
        {
            StreamReader sr = null;

            if (File.Exists(settingfile))
            {
                try
                {
                    sr = new StreamReader(settingfile);
                    String s = null;
                    while ((s = sr.ReadLine()) != null)
                    {
                        string[] stra = s.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                        if (stra.Length != 2) throw new BadSettingFileException();
                        SetSetting(stra[0], stra[1]);
                    }

                    return true;
                }
                catch (IOException ex)
                {
                    Console.WriteLine(ex.Message);
                }
                catch (BadSettingFileException ex)
                {
                    Console.WriteLine(ex.Message);
                }
                finally
                {
                    if (sr != null) sr.Close();
                    if (sr != null) sr.Dispose();
                    sr = null;
                }
            }
            else
            {
                InitSetting();
            }

            return false;
        }

        /// <summary>
        /// Inicializacia nastavenie
        /// </summary>
        /// <param name="dflt">Defaultna struktura, kde budu nastavenia ulozene </param>
        private static void InitSetting()
        {
            sett.Clear();
            sett.Add("OnlyFromProject", "0");
            sett.Add("IgnoreUnusedVar", "1");
            sett.Add("IgnoreUnusedFunc", "1");
            sett.Add("NumOfCallFunc", "0");
            sett.Add("NumOfReadVar", "0");
            sett.Add("NumOfWriteVar", "0");
            sett.Add("NumOfChangeVar", "0");
            sett.Add("OutputFormat", "PDF");
            sett.Add("ExportPath", Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\CodeVisual");
            sett.Add("OpenAfterExport", "1");
            LoadSettingFromDictionary();
        }

        private static void LoadSettingFromDictionary()
        {
            if (sett["OnlyFromProject"] == "1")
                onlyFromProject = true;
            else
                onlyFromProject = false;

            if (sett["IgnoreUnusedVar"] == "1")
                IgnoreUnusedVar = true;
            else
                IgnoreUnusedVar = false;

            if (sett["IgnoreUnusedFunc"] == "1")
                IgnoreUnusedFunc = true;
            else
                IgnoreUnusedFunc = false;

            NumCall = Convert.ToInt32(sett["NumOfCallFunc"]);
            NumRead = Convert.ToInt32(sett["NumOfReadVar"]); ;
            NumWrite = Convert.ToInt32(sett["NumOfWriteVar"]); ;
            NumChange = Convert.ToInt32(sett["NumOfChangeVar"]);

            String s = sett["OutputFormat"];
            if (s == "PDF") format = OutputFormat.PDF;
            else
                if (s == "SVG") format = OutputFormat.SVG;
                else
                    if (s == "HTML") format = OutputFormat.HTML;

            outPath = sett["ExportPath"];

            if (sett["OpenAfterExport"] == "1")
                openAfterExport = true;
            else
                openAfterExport = false;
        }

        /// <summary>
        /// Ukladanie nastavenia do suboru
        /// </summary>
        public static bool Save()
        {
            StreamWriter sw = null;
            bool correct = true;
            try
            {
                Directory.CreateDirectory("setting");
                sw = new StreamWriter(settingfile);
                foreach (KeyValuePair<string, string> p in sett)
                {
                    sw.WriteLine("{0}={1}", p.Key, p.Value);
                }
            }
            catch (Exception)
            {
                correct = false;
            }
            finally
            {
                if (sw != null)
                {
                    sw.Close();
                    sw.Dispose();
                }
            }
            return correct;
        }

        /// <summary>
        /// Zisti hodnotu nastavenia
        /// </summary>
        /// <param name="settingName">Nazov nastavenia</param>
        /// <returns></returns>
        public static string GetSetting(string settingName)
        {
            if (sett.ContainsKey(settingName))
            {
                return sett[settingName];
            }
            return null;
        }

        public static void SetSetting(string settingName, string Value)
        {
            if (sett.ContainsKey(settingName))
            {
                sett[settingName] = Value;
            }
            else
            {
                sett.Add(settingName, Value);
            }
        }

        public static void LoadDefaultSetting()
        {
            InitSetting();
            LoadSettingFromDictionary();
        }

    }
}